home *** CD-ROM | disk | FTP | other *** search
/ Giga Games 1 / Giga Games.iso / net / hack / 3_1_3 / win / x11 / winx.c < prev   
Encoding:
C/C++ Source or Header  |  1993-04-25  |  48.0 KB  |  1,779 lines

  1. /*    SCCS Id: @(#)winX.c    3.1    93/02/17          */
  2. /* Copyright (c) Dean Luick, 1992                  */
  3. /* NetHack may be freely redistributed.  See license for details. */
  4.  
  5. /*
  6.  * "Main" file for the X window-port.  This contains most of the interface
  7.  * routines.  Please see doc/window.doc for an description of the window
  8.  * interface.
  9.  */
  10.  
  11. #ifndef SYSV
  12. #define PRESERVE_NO_SYSV    /* X11 include files may define SYSV */
  13. #endif
  14.  
  15. #ifdef MSDOS            /* from compiler */
  16. #define SHORT_FILENAMES
  17. #endif
  18.  
  19. #include <X11/Intrinsic.h>
  20. #include <X11/StringDefs.h>
  21. #include <X11/Shell.h>
  22. #include <X11/Xaw/AsciiText.h>
  23. #include <X11/Xaw/Label.h>
  24. #include <X11/Xaw/Form.h>
  25. #include <X11/Xaw/Cardinals.h>
  26. #include <X11/Xatom.h>
  27. #include <X11/Xos.h>
  28.  
  29. /* for color support; should be ifdef TEXTCOLOR, but must come before hack.h */
  30. #ifdef SHORT_FILENAMES
  31. #include <X11/IntrinsP.h>
  32. #else
  33. #include <X11/IntrinsicP.h>
  34. #endif
  35.  
  36. #ifdef PRESERVE_NO_SYSV
  37. # ifdef SYSV
  38. #  undef SYSV
  39. # endif
  40. # undef PRESERVE_NO_SYSV
  41. #endif
  42.  
  43. #include "hack.h"
  44. #include "winX.h"
  45.  
  46. /* Should be defined in <X11/Intrinsic.h> but you never know */
  47. #ifndef XtSpecificationRelease
  48. #define XtSpecificationRelease 0
  49. #endif
  50.  
  51. /*
  52.  * Icons.
  53.  */
  54. #include "../win/X11/nh72icon"
  55. #include "../win/X11/nh56icon"
  56. #include "../win/X11/nh32icon"
  57.  
  58. static struct icon_info {
  59.     const char *name;
  60.     char *bits;
  61.     unsigned width, height;
  62. } icon_data[] = {
  63.     { "nh72", nh72icon_bits, nh72icon_width, nh72icon_height },
  64.     { "nh56", nh56icon_bits, nh56icon_width, nh56icon_height },
  65.     { "nh32", nh32icon_bits, nh32icon_width, nh32icon_height },
  66.     { NULL, NULL, 0, 0 }
  67. };
  68.  
  69. /*
  70.  * Private global variables (shared among the window port files).
  71.  */
  72. struct xwindow window_list[MAX_WINDOWS];
  73. AppResources appResources;
  74. void (*input_func)();
  75. int click_x, click_y, click_button;    /* Click position on a map window   */
  76.                     /* (filled by set_button_values()). */
  77.  
  78.  
  79. /* Interface definition, for windows.c */
  80. struct window_procs X11_procs = {
  81.     "X11",
  82.     X11_init_nhwindows,
  83.     X11_player_selection,
  84.     X11_askname,
  85.     X11_get_nh_event,
  86.     X11_exit_nhwindows,
  87.     X11_suspend_nhwindows,
  88.     X11_resume_nhwindows,
  89.     X11_create_nhwindow,
  90.     X11_clear_nhwindow,
  91.     X11_display_nhwindow,
  92.     X11_destroy_nhwindow,
  93.     X11_curs,
  94.     X11_putstr,
  95.     X11_display_file,
  96.     X11_start_menu,
  97.     X11_add_menu,
  98.     X11_end_menu,
  99.     X11_select_menu,
  100.     X11_update_inventory,
  101.     X11_mark_synch,
  102.     X11_wait_synch,
  103. #ifdef CLIPPING
  104.     X11_cliparound,
  105. #endif
  106.     X11_print_glyph,
  107.     X11_raw_print,
  108.     X11_raw_print_bold,
  109.     X11_nhgetch,
  110.     X11_nh_poskey,
  111.     X11_nhbell,
  112.     X11_doprev_message,
  113.     X11_yn_function,
  114.     X11_getlin,
  115. #ifdef COM_COMPL
  116.     X11_get_ext_cmd,
  117. #endif /* COM_COMPL */
  118.     X11_number_pad,
  119.     X11_delay_output,
  120. #ifdef CHANGE_COLOR    /* only a Mac option currently */
  121.     donull,
  122.     donull,
  123. #endif
  124.     /* other defs that really should go away (they're tty specific) */
  125.     X11_start_screen,
  126.     X11_end_screen,
  127.     genl_outrip,
  128. };
  129.  
  130. /*
  131.  * Local functions.
  132.  */
  133. static void FDECL(dismiss_file, (Widget, XEvent*, String*, Cardinal*));
  134. static void FDECL(delete_file, (Widget, XEvent*, String*, Cardinal*));
  135. static void FDECL(yn_key, (Widget, XEvent*, String*, Cardinal*));
  136. static void FDECL(yn_delete, (Widget, XEvent*, String*, Cardinal*));
  137. static void FDECL(askname_delete, (Widget, XEvent*, String*, Cardinal*));
  138. static void FDECL(getline_delete, (Widget, XEvent*, String*, Cardinal*));
  139. static void FDECL(X11_hangup, (Widget, XEvent*, String*, Cardinal*));
  140. static int FDECL(input_event, (int));
  141. static void FDECL(win_visible, (Widget,XtPointer,XEvent *,Boolean *));
  142. static void NDECL(init_standard_windows);
  143.  
  144.  
  145. /*
  146.  * Local variables.
  147.  */
  148. static boolean x_inited = FALSE;    /* TRUE if window system is set up. */
  149. static winid message_win = WIN_ERR,    /* These are the winids of the        */
  150.          map_win     = WIN_ERR,    /*   message, map, and status        */
  151.          status_win  = WIN_ERR;    /*   windows, when they are created */
  152.                     /*   in init_windows().            */
  153. static Pixmap icon_pixmap = None;    /* Pixmap for icon.            */
  154.  
  155. /*
  156.  * Find the window structure that corresponds to the given widget.  Note
  157.  * that this is not the popup widget, nor the viewport, but the child.
  158.  */
  159. struct xwindow *
  160. find_widget(w)
  161.     Widget w;
  162. {
  163.     int windex;
  164.     struct xwindow *wp;
  165.  
  166.     /* This is sad.  Search to find the corresponding window. */
  167.     for (windex = 0, wp = window_list; windex < MAX_WINDOWS; windex++, wp++)
  168.     if (wp->type != NHW_NONE &&
  169.         (wp->w == w || (wp->w && XtParent(wp->w) == w))) break;
  170.     if (windex == MAX_WINDOWS) panic("find_widget:  can't match widget");
  171.     return wp;
  172. }
  173.  
  174. /*
  175.  * Find a free window slot for use.
  176.  */
  177. static winid
  178. find_free_window()
  179. {
  180.     int windex;
  181.     struct xwindow *wp;
  182.  
  183.     for (windex = 0, wp = &window_list[0]; windex < MAX_WINDOWS; windex++, wp++)
  184.     if (wp->type == NHW_NONE) break;
  185.  
  186.     if (windex == MAX_WINDOWS)
  187.     panic("find_free_window: no free windows!");
  188.     return (winid) windex;
  189. }
  190.  
  191. #ifdef TEXTCOLOR
  192. /*
  193.  * Color conversion.  The default X11 color converters don't try very
  194.  * hard to find matching colors in PseudoColor visuals.  If they can't
  195.  * allocate the exact color, they puke and give you something stupid.
  196.  * This is an attempt to find some close readonly cell and use it.
  197.  */
  198. XtConvertArgRec const nhcolorConvertArgs[] = {
  199.     {XtWidgetBaseOffset, (XtPointer)XtOffset(Widget, core.screen),
  200.      sizeof(Screen *)},
  201.     {XtWidgetBaseOffset, (XtPointer)XtOffset(Widget, core.colormap),
  202.      sizeof(Colormap)}
  203. };
  204.  
  205. #define done(type, value) \
  206.     {                            \
  207.         if (toVal->addr != NULL) {                \
  208.         if (toVal->size < sizeof(type)) {        \
  209.             toVal->size = sizeof(type);            \
  210.             return False;                \
  211.         }                        \
  212.         *(type*)(toVal->addr) = (value);        \
  213.         }                            \
  214.         else {                        \
  215.         static type static_val;                \
  216.         static_val = (value);                \
  217.         toVal->addr = (genericptr_t)&static_val;    \
  218.         }                            \
  219.         toVal->size = sizeof(type);                \
  220.         return True;                    \
  221.     }
  222.  
  223. /* decl.h declares these, but it screws up structure references -dlc */
  224. #undef red
  225. #undef green
  226. #undef blue
  227.  
  228. /* Return True if something close was found. */
  229. Boolean
  230. nhCloseColor(screen, colormap, str, color)
  231. Screen     *screen;    /* screen to use */
  232. Colormap colormap;    /* the colormap to use */
  233. char     *str;        /* color name */
  234. XColor   *color;    /* the X color structure; changed only if successful */
  235. {
  236.     int        ncells;
  237.     long    cdiff = 16777216; /* 2^24; hopefully our map is smaller */
  238.     XColor    tmp;
  239.     static    XColor *table = 0;
  240.     register    i, j;
  241.     register long tdiff;
  242.  
  243.     /* if the screen doesn't have a big colormap, don't waste our time */
  244.     /* or if it's huge, and _some_ match should have been possible */
  245.     if((ncells = CellsOfScreen(screen)) < 256 || ncells > 4096)
  246.     return False;
  247.  
  248.     if (!XParseColor(DisplayOfScreen(screen), colormap, str, &tmp))
  249.     return False;
  250.  
  251.     if (!table) {
  252.     table = (XColor *) XtCalloc(ncells, sizeof(XColor));
  253.     for(i=0; i<ncells; i++)
  254.         table[i].pixel = i;
  255.     XQueryColors(DisplayOfScreen(screen), colormap, table, ncells);
  256.     }
  257.  
  258.     /* go thru cells and look for the one with smallest diff */
  259.     /* diff is calculated abs(reddiff)+abs(greendiff)+abs(bluediff) */
  260.     /* a more knowledgeable color person might improve this -dlc */
  261.     for(i=0; i<ncells; i++) {
  262.     if(table[i].flags == tmp.flags) {
  263.         j = (int)table[i].red - (int)tmp.red;
  264.         if(j < 0) j = -j;
  265.         tdiff = j;
  266.         j = (int)table[i].green - (int)tmp.green;
  267.         if(j < 0) j = -j;
  268.         tdiff += j;
  269.         j = (int)table[i].blue - (int)tmp.blue;
  270.         if(j < 0) j = -j;
  271.         tdiff += j;
  272.         if(tdiff < cdiff) {
  273.         cdiff = tdiff;
  274.         tmp.pixel = i; /* table[i].pixel == i */
  275.         }
  276.     }
  277.     }
  278.  
  279.     if(cdiff == 16777216) return False;    /* nothing found?! */
  280.  
  281.     /*
  282.      * Found something.  Return it and mark this color as used to avoid
  283.      * reuse.  Reuse causes major contrast problems :-)
  284.      */
  285.     color->pixel = tmp.pixel;
  286.     table[tmp.pixel].flags = 0;
  287.     return True;
  288. }
  289.  
  290. Boolean
  291. nhCvtStringToPixel(dpy, args, num_args, fromVal, toVal, closure_ret)
  292. Display*    dpy;
  293. XrmValuePtr    args;
  294. Cardinal    *num_args;
  295. XrmValuePtr    fromVal;
  296. XrmValuePtr    toVal;
  297. XtPointer    *closure_ret;
  298. {
  299.     String        str = (String)fromVal->addr;
  300.     XColor        screenColor;
  301.     XColor        exactColor;
  302.     Screen        *screen;
  303.     XtAppContext    app = XtDisplayToApplicationContext(dpy);
  304.     Colormap        colormap;
  305.     Status        status;
  306.     String          params[1];
  307.     Cardinal        num_params=1;
  308.  
  309.     if (*num_args != 2) {
  310.      XtAppWarningMsg(app, "wrongParameters", "cvtStringToPixel",
  311.     "XtToolkitError",
  312.     "String to pixel conversion needs screen and colormap arguments",
  313.     (String *)0, (Cardinal *)0);
  314.      return False;
  315.     }
  316.  
  317.     screen = *((Screen **) args[0].addr);
  318.     colormap = *((Colormap *) args[1].addr);
  319.  
  320.     /* If Xt colors, use the Xt routine and hope for the best */
  321. #if (XtSpecificationRelease >= 5)
  322.     if ((strcmpi(str, XtDefaultBackground) == 0) ||
  323.     (strcmpi(str, XtDefaultForeground) == 0)) {
  324.     return
  325.       XtCvtStringToPixel(dpy, args, num_args, fromVal, toVal, closure_ret);
  326.     }
  327. #else
  328.     if (strcmpi(str, XtDefaultBackground) == 0) {
  329.     *closure_ret = (char*)False;
  330.     done(Pixel, WhitePixelOfScreen(screen));
  331.     }
  332.     if (strcmpi(str, XtDefaultForeground) == 0) {
  333.     *closure_ret = (char*)False;
  334.     done(Pixel, BlackPixelOfScreen(screen));
  335.     }
  336. #endif
  337.  
  338.     status = XAllocNamedColor(DisplayOfScreen(screen), colormap,
  339.                   (char*)str, &screenColor, &exactColor);
  340.     if (status == 0) {
  341.     String msg, type;
  342.     params[0] = str;
  343.     /* Server returns a specific error code but Xlib discards it.  Ugh */
  344.     if (XLookupColor(DisplayOfScreen(screen), colormap, (char*)str,
  345.              &exactColor, &screenColor)) {
  346.         /* try to find another color that will do */
  347.         if (nhCloseColor(screen, colormap, (char*) str, &screenColor)) {
  348.         *closure_ret = (char*)True;
  349.         done(Pixel, screenColor.pixel);
  350.         }
  351.         type = "noColormap";
  352.         msg = "Cannot allocate colormap entry for \"%s\"";
  353.     }
  354.     else {
  355.         type = "badValue";
  356.         msg = "Color name \"%s\" is not defined";
  357.     }
  358.  
  359.     XtAppWarningMsg(app, type, "cvtStringToPixel",
  360.             "XtToolkitError", msg, params, &num_params);
  361.     *closure_ret = False;
  362.     return False;
  363.     } else {
  364.     *closure_ret = (char*)True;
  365.     done(Pixel, screenColor.pixel);
  366.     }
  367. }
  368.  
  369. /* ARGSUSED */
  370. static void
  371. nhFreePixel(app, toVal, closure, args, num_args)
  372. XtAppContext    app;
  373. XrmValuePtr    toVal;
  374. XtPointer    closure;
  375. XrmValuePtr    args;
  376. Cardinal    *num_args;
  377. {
  378.     Screen        *screen;
  379.     Colormap        colormap;
  380.  
  381.     if (*num_args != 2) {
  382.      XtAppWarningMsg(app, "wrongParameters",
  383.              "freePixel", "XtToolkitError",
  384.              "Freeing a pixel requires screen and colormap arguments",
  385.              (String *)0, (Cardinal *)0);
  386.      return;
  387.     }
  388.  
  389.     screen = *((Screen **) args[0].addr);
  390.     colormap = *((Colormap *) args[1].addr);
  391.  
  392.     if (closure) {
  393.     XFreeColors( DisplayOfScreen(screen), colormap,
  394.              (unsigned long*)toVal->addr, 1, (unsigned long)0
  395.             );
  396.     }
  397. }
  398. #endif /* TEXTCOLOR */
  399.  
  400. /* Global Functions ======================================================== */
  401. void
  402. X11_raw_print(str)
  403.     const char *str;
  404. {
  405.     (void) puts(str);
  406. }
  407.  
  408. void
  409. X11_raw_print_bold(str)
  410.     const char *str;
  411. {
  412.     (void) puts(str);
  413. }
  414.  
  415. void
  416. X11_curs(window, x, y)
  417.     winid window;
  418.     int x, y;
  419. {
  420.     check_winid(window);
  421.  
  422.     if (x < 0 || x >= COLNO) {
  423.     impossible("curs:  bad x value [%d]", x);
  424.     x = 0;
  425.     }
  426.     if (y < 0 || y >= ROWNO) {
  427.     impossible("curs:  bad y value [%d]", y);
  428.     y = 0;
  429.     }
  430.  
  431.     window_list[window].cursx = x;
  432.     window_list[window].cursy = y;
  433. }
  434.  
  435. void
  436. X11_putstr(window, attr, str)
  437.     winid window;
  438.     int attr;
  439.     const char *str;
  440. {
  441.     winid new_win;
  442.     struct xwindow *wp;
  443.  
  444.     check_winid(window);
  445.     wp = &window_list[window];
  446.  
  447.     switch (wp->type) {
  448.     case NHW_MESSAGE:
  449.         Strcpy(toplines, str);    /* for Norep(). */
  450.         append_message(wp, str);
  451.         break;
  452.     case NHW_STATUS:
  453.         adjust_status(wp, str);
  454.         break;
  455.     case NHW_MAP:
  456.         impossible("putstr: called on map window \"%s\"", str);
  457.         break;
  458.     case NHW_MENU:
  459.         if (wp->menu_information->is_menu) {
  460.         impossible(
  461.             "putstr:  called on a menu window, \"%s\" discarded",
  462.             str);
  463.         break;
  464.         }
  465.         /*
  466.          * Change this menu window into a text window by creating a
  467.          * new text window, then copying it to this winid.
  468.          */
  469.         new_win = X11_create_nhwindow(NHW_TEXT);
  470.         X11_destroy_nhwindow(window);
  471.         *wp = window_list[new_win];
  472.         window_list[new_win].type = NHW_NONE;    /* allow re-use */
  473.         /* fall though to add text */
  474.     case NHW_TEXT:
  475.         add_to_text_window(wp, attr, str);
  476.         break;
  477.     default:
  478.         impossible("putstr: unknown window type [%d] \"%s\"",
  479.                                 wp->type, str);
  480.     }
  481. }
  482.  
  483. /* We do event processing as a callback, so this is a null routine. */
  484. void X11_get_nh_event() { return; }
  485.  
  486. int
  487. X11_nhgetch()
  488. {
  489.     return input_event(EXIT_ON_KEY_PRESS);
  490. }
  491.  
  492.  
  493. int
  494. X11_nh_poskey(x, y, mod)
  495.     int *x, *y, *mod;
  496. {
  497.     int val = input_event(EXIT_ON_KEY_OR_BUTTON_PRESS);
  498.  
  499.     if (val == 0) {    /* user clicked on a map window */
  500.     *x   = click_x;
  501.     *y   = click_y;
  502.     *mod = click_button;
  503.     }
  504.     return val;
  505. }
  506.  
  507.  
  508. winid
  509. X11_create_nhwindow(type)
  510.     int type;
  511. {
  512.     winid window;
  513.     struct xwindow *wp;
  514.  
  515.     if (!x_inited)
  516.     panic("create_nhwindow:  windows not initialized");
  517.  
  518.     /*
  519.      * We have already created the standard message, map, and status
  520.      * windows in the window init routine.  The first window of that
  521.      * type to be created becomes the standard.
  522.      *
  523.      * A better way to do this would be to say that init_nhwindows()
  524.      * has already defined these three windows.
  525.      */
  526.     if (type == NHW_MAP && map_win != WIN_ERR) {
  527.     window = map_win;
  528.     map_win = WIN_ERR;
  529.     return window;
  530.     }
  531.     if (type == NHW_MESSAGE && message_win != WIN_ERR) {
  532.     window = message_win;
  533.     message_win = WIN_ERR;
  534.     return window;
  535.     }
  536.     if (type == NHW_STATUS && status_win != WIN_ERR) {
  537.     window = status_win;
  538.     status_win = WIN_ERR;
  539.     return window;
  540.     }
  541.  
  542.     window = find_free_window();
  543.     wp = &window_list[window];
  544.  
  545.     /* The create routines will set type, popup, w, and Win_info. */
  546.     wp->prevx = wp->prevy = wp->cursx = wp->cursy =
  547.                 wp->pixel_width = wp->pixel_height = 0;
  548.  
  549.     switch (type) {
  550.     case NHW_MAP:
  551.         create_map_window(wp, TRUE, (Widget) 0);
  552.         break;
  553.     case NHW_MESSAGE:
  554.         create_message_window(wp, TRUE, (Widget) 0);
  555.         break;
  556.     case NHW_STATUS:
  557.         create_status_window(wp, TRUE, (Widget) 0);
  558.         break;
  559.     case NHW_MENU:
  560.         create_menu_window(wp);
  561.         break;
  562.     case NHW_TEXT:
  563.         create_text_window(wp);
  564.         break;
  565.     default:
  566.         panic("create_nhwindow: unknown type [%d]\n", type);
  567.         break;
  568.     }
  569.     return window;
  570. }
  571.  
  572. void
  573. X11_clear_nhwindow(window)
  574.     winid window;
  575. {
  576.     struct xwindow *wp;
  577.  
  578.     check_winid(window);
  579.     wp = &window_list[window];
  580.  
  581.     switch (wp->type) {
  582.     case NHW_MAP:
  583.         clear_map_window(wp);
  584.         break;
  585.     case NHW_STATUS:
  586.     case NHW_TEXT:
  587.     case NHW_MENU:
  588.     case NHW_MESSAGE:
  589.         /* do nothing for these window types */
  590.         break;
  591.     default:
  592.         panic("clear_nhwindow: unknown window type [%d]\n", wp->type);
  593.         break;
  594.     }
  595. }
  596.  
  597. void
  598. X11_display_nhwindow(window, blocking)
  599.     winid window;
  600.     boolean blocking;
  601. {
  602.     struct xwindow *wp;
  603.     check_winid(window);
  604.  
  605.     wp = &window_list[window];
  606.  
  607.     switch (wp->type) {
  608.     case NHW_MAP:
  609.         if (wp->popup)
  610.         nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w);
  611.         /*else
  612.          *  XtMapWidget(toplevel);
  613.          *
  614.          * We don't need to do the above because we never have
  615.          * MappedWhenManaged unset because the DEC server doesn't
  616.          * like it.  See comment above XtSetMappedWhenManaged() in
  617.          * init_standard_windows().
  618.          */
  619.         display_map_window(wp);    /* flush map */
  620.  
  621.         /*
  622.          * We need to flush the message window here due to the way the tty
  623.          * port is set up.  To flush a window, you need to call this
  624.          * routine.  However, the tty port _pauses_ with a --more-- if we
  625.          * do a display_nhwindow(WIN_MESSAGE, FALSE).  Thus, we can't call
  626.          * display_nhwindow(WIN_MESSAGE,FALSE) in parse() because then we
  627.          * get a --more-- after every line.
  628.          *
  629.          * Perhaps the window document should mention that when the map
  630.          * is flushed, everything on the three main windows should be
  631.          * flushed.  Note: we don't need to flush the status window
  632.          * because we don't buffer changes.
  633.          */
  634.         if (WIN_MESSAGE != WIN_ERR)
  635.         display_message_window(&window_list[WIN_MESSAGE]);
  636.         if (blocking)
  637.         (void) x_event(EXIT_ON_KEY_OR_BUTTON_PRESS);
  638.         break;
  639.     case NHW_MESSAGE:
  640.         if (wp->popup)
  641.          nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w);
  642.         /*else
  643.          *    XtMapWidget(toplevel);
  644.          *
  645.          * See comment in NHW_MAP case.
  646.          */
  647.  
  648.         display_message_window(wp);    /* flush messages */
  649.         break;
  650.     case NHW_STATUS:
  651.         if (wp->popup)
  652.         nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w);
  653.         /*else
  654.          *    XtMapWidget(toplevel);
  655.          *
  656.          * See comment in NHW_MAP case.
  657.          */
  658.  
  659.         break;            /* no flushing necessary */
  660.     case NHW_MENU:
  661.         (void) X11_select_menu(window); /* pop up menu (global routine) */
  662.         break;
  663.     case NHW_TEXT:
  664.         display_text_window(wp, blocking);    /* pop up text window */
  665.         break;
  666.     default:
  667.         panic("display_nhwindow: unknown window type [%d]\n", wp->type);
  668.         break;
  669.     }
  670. }
  671.  
  672. void
  673. X11_destroy_nhwindow(window)
  674.     winid window;
  675. {
  676.     struct xwindow *wp;
  677.     check_winid(window);
  678.  
  679.     /*
  680.      * "Zap" known windows, but don't destroy them.  We need to keep the
  681.      * toplevel widget popped up so that later windows (e.g. tombstone)
  682.      * are visible on DECWindow systems.  This is due to the virtual
  683.      * roots that the DECWindow wm creates.
  684.      */
  685.     if (window == WIN_MESSAGE) {
  686.     WIN_MESSAGE = WIN_ERR;
  687.     flags.window_inited = 0;
  688.     return;
  689.     } else if (window == WIN_MAP) {
  690.     WIN_MAP = WIN_ERR;
  691.     return;
  692.     } else if (window == WIN_STATUS) {
  693.     WIN_STATUS = WIN_ERR;
  694.     return;
  695.     } else if (window == WIN_INVEN) {
  696.     WIN_INVEN = WIN_ERR;
  697.     return;
  698.     }
  699.  
  700.     wp = &window_list[window];
  701.  
  702.     switch (wp->type) {
  703.     case NHW_MAP:
  704.         destroy_map_window(wp);
  705.         break;
  706.     case NHW_MENU:
  707.         destroy_menu_window(wp);
  708.         break;
  709.     case NHW_TEXT:
  710.         destroy_text_window(wp);
  711.         break;
  712.     case NHW_STATUS:
  713.         destroy_status_window(wp);
  714.         break;
  715.     case NHW_MESSAGE:
  716.         destroy_message_window(wp);
  717.         break;
  718.     default:
  719.         panic("destroy_nhwindow: unknown window type [%d]", wp->type);
  720.         break;
  721.     }
  722. }
  723.  
  724. /* We don't implement a continous invent screen, so this is null. */
  725. void X11_update_inventory() { return; }
  726.  
  727. /* The current implementation has all of the saved lines on the screen. */
  728. int X11_doprev_message() { return 0; }
  729.  
  730. void
  731. X11_nhbell()
  732. {
  733.     /* We can't use XBell until toplevel has been initialized. */
  734.     if (x_inited)
  735.     XBell(XtDisplay(toplevel), 0);
  736.     /* else print ^G ?? */
  737. }
  738.  
  739. void X11_mark_synch()
  740. {
  741.     if (x_inited) {
  742.     /*
  743.      * the window document is a bit unclear about the status of text
  744.      * that has been pline()d but not displayed w/display_nhwindow(),
  745.      * though the main code and tty code assume that a pline() followed
  746.      * by mark_synch() results in the text being seen, even if
  747.      * display_nhwindow() wasn't called.  Duplicate this behavior.
  748.      */
  749.     if (WIN_MESSAGE != WIN_ERR)
  750.         display_message_window(&window_list[WIN_MESSAGE]);
  751.     XSync(XtDisplay(toplevel), False);
  752.     }
  753. }
  754.  
  755. void X11_wait_synch() {if (x_inited) XFlush(XtDisplay(toplevel)); }
  756.  
  757.  
  758. /* Both resume_ and suspend_ are called from ioctl.c and unixunix.c. */
  759. void X11_resume_nhwindows() { return; }
  760.  
  761. /* ARGSUSED */
  762. void X11_suspend_nhwindows(str) const char *str; { return; }
  763.  
  764. /* Under X, we don't need to initialize the number pad. */
  765. /* ARGSUSED */
  766. void X11_number_pad(state) int state; { return; } /* called from options.c */
  767.  
  768.  
  769. void X11_start_screen() { return; } /* called from setftty() in unixtty.c */
  770. void X11_end_screen() { return; }   /* called from settty() in unixtty.c */
  771.  
  772.  
  773. /* init and exit nhwindows ------------------------------------------------- */
  774.  
  775. XtAppContext app_context;        /* context of application */
  776. Widget         toplevel = (Widget) 0;    /* toplevel widget */
  777. Atom         wm_delete_window;        /* To pop-down windows */
  778.  
  779. static XtActionsRec actions[] = {
  780.     {"dismiss_file",    dismiss_file},    /* action for file viewing widget */
  781.     {"delete_file",    delete_file},    /* action for file delete-window */
  782.     {"dismiss_text",    dismiss_text},    /* button action for text widget */
  783.     {"delete_text",    delete_text},    /* delete action for text widget */
  784.     {"key_dismiss_text",key_dismiss_text},/* key action for text widget */
  785.     {"menu_key",    menu_key},    /* action for menu accelerators */
  786.     {"yn_key",        yn_key},    /* action for yn accelerators */
  787.     {"yn_delete",    yn_delete},    /* action for yn delete-window */
  788.     {"askname_delete",    askname_delete},/* action for askname delete-window */
  789.     {"getline_delete",    getline_delete},/* action for getline delete-window */
  790.     {"menu_delete",    menu_delete},    /* action for menu delete-window */
  791.     {"ec_key",        ec_key},    /* action for extended commands */
  792.     {"ec_delete",    ec_delete},    /* action for ext-com menu delete */
  793.     {"ps_key",        ps_key},    /* action for player selection */
  794.     {"X11_hangup",    X11_hangup},    /* action for delete of top-level */
  795. };
  796.  
  797. static XtResource resources[] = {
  798.     { "slow", "Slow", XtRBoolean, sizeof(Boolean),
  799.       XtOffset(AppResources *,slow), XtRString, "False" },
  800.     { "autofocus", "AutoFocus", XtRBoolean, sizeof(Boolean),
  801.       XtOffset(AppResources *,autofocus), XtRString, "False" },
  802.     { "message_line", "Message_line", XtRBoolean, sizeof(Boolean),
  803.       XtOffset(AppResources *,message_line), XtRString, "False" },
  804.     { "icon", "Icon", XtRString, sizeof(String),
  805.       XtOffset(AppResources *,icon), XtRString, "nh72" },
  806. };
  807.  
  808. void
  809. X11_init_nhwindows()
  810. {
  811.     static const char *banner_text[] = {
  812.     "NetHack",
  813.     "Copyright 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993",
  814.     "by Stichting Mathematisch Centrum and M. Stephenson.",
  815.     "See license for details.",
  816.     "",
  817.     "",
  818.     0
  819.     };
  820.     static const char *av[] = { "nethack" };
  821.     register const char **pp;
  822.     int i;
  823.     Cardinal num_args;
  824.     Arg args[4];
  825.  
  826.     /* Init windows to nothing. */
  827.     for (i = 0; i < MAX_WINDOWS; i++)
  828.     window_list[i].type = NHW_NONE;
  829.  
  830.     XSetIOErrorHandler((XIOErrorHandler) hangup);
  831.  
  832.     i = 1;
  833.     num_args = 0;
  834.     XtSetArg(args[num_args], XtNallowShellResize, True);    num_args++;
  835.     toplevel = XtAppInitialize(
  836.             &app_context,
  837.             "NetHack",        /* application class */
  838.             (XrmOptionDescList)0, 0,    /* options list */
  839.             &i, av,        /* command line args */
  840.             (String *)0,    /* fallback resources */
  841.             (ArgList)args, num_args);
  842.     XtOverrideTranslations(toplevel,
  843.     XtParseTranslationTable("<Message>WM_PROTOCOLS: X11_hangup()"));
  844.  
  845.     /* We don't need to realize the top level widget. */
  846.  
  847. #ifdef TEXTCOLOR
  848.     /* add new color converter to deal with overused colormaps */
  849.     XtSetTypeConverter(XtRString, XtRPixel, nhCvtStringToPixel,
  850.                (XtConvertArgList)nhcolorConvertArgs, 
  851.                XtNumber(nhcolorConvertArgs),
  852.                XtCacheByDisplay, nhFreePixel);
  853. #endif /* TEXTCOLOR */
  854.  
  855.     /* Register the actions mentioned in "actions". */
  856.     XtAppAddActions(app_context, actions, XtNumber(actions));
  857.  
  858.     /* Get application-wide resources */
  859.     XtGetApplicationResources(toplevel, (XtPointer)&appResources,
  860.                   resources, XtNumber(resources),
  861.                   (ArgList)0, ZERO);
  862.  
  863.     /* Initialize other things. */
  864.     init_standard_windows();
  865.     init_extended_commands_popup();
  866.  
  867.     /* Give the window manager an icon to use;  toplevel must be realized. */
  868.     if (appResources.icon && *appResources.icon) {
  869.     struct icon_info *ip;
  870.  
  871.     for (ip = icon_data; ip->name; ip++)
  872.         if (!strcmp(appResources.icon, ip->name)) {
  873.         icon_pixmap = XCreateBitmapFromData(XtDisplay(toplevel),
  874.                     XtWindow(toplevel),
  875.                     ip->bits, ip->width, ip->height);
  876.         if (icon_pixmap != None) {
  877.             XWMHints hints;
  878.  
  879.             hints.flags = IconPixmapHint;
  880.             hints.icon_pixmap = icon_pixmap;
  881.             XSetWMHints(XtDisplay(toplevel),
  882.                 XtWindow(toplevel), &hints);
  883.         }
  884.         break;
  885.         }
  886.     }
  887.  
  888.     x_inited = TRUE;    /* X is now initialized */
  889.  
  890.     /* Display the startup banner in the message window. */
  891.     for (pp = banner_text; *pp; pp++)
  892.     X11_putstr(WIN_MESSAGE, 0, *pp);
  893. }
  894.  
  895. /*
  896.  * Let the OS take care of almost everything.  This includes the "main"
  897.  * three windows:  message, map, and status.  Note that if I destroy one of
  898.  * the three main windows, they all will be destroyed, due to their shared
  899.  * parent.  I currently don't check such a thing occuring, so the whole mess
  900.  * will probably crash&burn if I tried it.
  901.  */
  902. /* ARGSUSED */
  903. void X11_exit_nhwindows(dummy)
  904.     const char *dummy;
  905. {
  906.     /* explicitly free the icon pixmap */
  907.     if (icon_pixmap != None) {
  908.     XFreePixmap(XtDisplay(toplevel), icon_pixmap);
  909.     icon_pixmap = None;
  910.     }
  911. }
  912.  
  913.  
  914. /* delay_output ------------------------------------------------------------ */
  915.  
  916. /*
  917.  * Timeout callback for delay_output().  Send a fake message to the map
  918.  * window.
  919.  */
  920. /* ARGSUSED */
  921. static void
  922. d_timeout(client_data, id)
  923.     XtPointer client_data;
  924.     XtIntervalId *id;
  925. {
  926.     XEvent event;
  927.     XClientMessageEvent *mesg;
  928.  
  929.     /* Set up a fake message to the event handler. */
  930.     mesg = (XClientMessageEvent *) &event;
  931.     mesg->type = ClientMessage;
  932.     mesg->message_type = XA_STRING;
  933.     mesg->format = 8;
  934.     XSendEvent(XtDisplay(window_list[WIN_MAP].w),
  935.         XtWindow(window_list[WIN_MAP].w),
  936.         False,
  937.         NoEventMask,
  938.         (XEvent*) mesg);
  939. }
  940.  
  941. /*
  942.  * Delay for 50ms.  This is not implemented asynch.  Maybe later.
  943.  * Start the timeout, then wait in the event loop.  The timeout
  944.  * function will send an event to the map window which will be waiting
  945.  * for a sent event.
  946.  */
  947. void
  948. X11_delay_output()
  949. {
  950.     if (!x_inited) return;
  951.  
  952.     (void) XtAppAddTimeOut(app_context, 30L, d_timeout, (XtPointer) 0);
  953.  
  954.     /* The timeout function will enable the event loop exit. */
  955.     (void) x_event(EXIT_ON_SENT_EVENT);
  956. }
  957.  
  958. /* X11_hangup -------------------------------------------------------------- */
  959. /* ARGSUSED */
  960. static void
  961. X11_hangup(w, event, params, num_params)
  962.     Widget w;
  963.     XEvent *event;
  964.     String *params;
  965.     Cardinal *num_params;
  966. {
  967.     (void) hangup();
  968. }
  969.  
  970. /* askname ----------------------------------------------------------------- */
  971. /* ARGSUSED */
  972. static void
  973. askname_delete(w, event, params, num_params)
  974.     Widget w;
  975.     XEvent *event;
  976.     String *params;
  977.     Cardinal *num_params;
  978. {
  979.     nh_XtPopdown(w);
  980.     (void) strcpy(plname, "Mumbles");    /* give them a name... ;-) */
  981.     exit_x_event = TRUE;
  982. }
  983.  
  984. /* Callback for askname dialog widget. */
  985. /* ARGSUSED */
  986. static void
  987. askname_done(w, client_data, call_data)
  988.     Widget w;
  989.     XtPointer client_data;
  990.     XtPointer call_data;
  991. {
  992.     int len;
  993.     char *s;
  994.     Widget dialog = (Widget) client_data;
  995.  
  996.     s = (char *) GetDialogResponse(dialog);
  997.  
  998.     len = strlen(s);
  999.     if (len == 0) {
  1000.     X11_nhbell();
  1001.     return;
  1002.     }
  1003.  
  1004.     /* Truncate name if necessary */
  1005.     if (len >= sizeof(plname)-1)
  1006.     len = sizeof(plname)-1;
  1007.  
  1008.     (void) strncpy(plname, s, len);
  1009.     plname[len] = '\0';
  1010.  
  1011.     nh_XtPopdown(XtParent(dialog));
  1012.     exit_x_event = TRUE;
  1013. }
  1014.  
  1015. void
  1016. X11_askname()
  1017. {
  1018.     Widget popup, dialog;
  1019.     Arg args[1];
  1020.  
  1021.     XtSetArg(args[0], XtNallowShellResize, True);
  1022.  
  1023.     popup = XtCreatePopupShell("askname", transientShellWidgetClass,
  1024.                    toplevel, args, ONE);
  1025.     XtOverrideTranslations(popup,
  1026.     XtParseTranslationTable("<Message>WM_PROTOCOLS: askname_delete()"));
  1027.  
  1028.     dialog = CreateDialog(popup, "dialog",
  1029.                     askname_done, (XtCallbackProc) 0);
  1030.  
  1031.     SetDialogPrompt(dialog, "What is your name?");    /* set prompt */
  1032.     SetDialogResponse(dialog, "");        /* set default answer */
  1033.  
  1034.     XtRealizeWidget(popup);
  1035.     positionpopup(popup, TRUE);        /* center,bottom */
  1036.  
  1037.     nh_XtPopup(popup, (int)XtGrabExclusive, dialog);
  1038.  
  1039.     /* The callback will enable the event loop exit. */
  1040.     (void) x_event(EXIT_ON_EXIT);
  1041. }
  1042.  
  1043.  
  1044. /* getline ----------------------------------------------------------------- */
  1045. /* This uses Tim Theisen's dialog widget set (from GhostView). */
  1046.  
  1047. static Widget getline_popup, getline_dialog;
  1048.  
  1049. #define CANCEL_STR "\033"
  1050. static char *getline_input;
  1051.  
  1052.  
  1053. /* Callback for getline dialog widget. */
  1054. /* ARGSUSED */
  1055. static void
  1056. done_button(w, client_data, call_data)
  1057.     Widget w;
  1058.     XtPointer client_data;
  1059.     XtPointer call_data;
  1060. {
  1061.     char *s;
  1062.     Widget dialog = (Widget) client_data;
  1063.  
  1064.     s = (char *) GetDialogResponse(dialog);
  1065.     Strcpy(getline_input, s);
  1066.     nh_XtPopdown(XtParent(dialog));
  1067.     exit_x_event = TRUE;
  1068. }
  1069.  
  1070. /* ARGSUSED */
  1071. static void
  1072. getline_delete(w, event, params, num_params)
  1073.     Widget w;
  1074.     XEvent *event;
  1075.     String *params;
  1076.     Cardinal *num_params;
  1077. {
  1078.     Strcpy(getline_input, CANCEL_STR);
  1079.     nh_XtPopdown(w);
  1080.     exit_x_event = TRUE;
  1081. }
  1082.  
  1083. /* Callback for getline dialog widget. */
  1084. /* ARGSUSED */
  1085. static void
  1086. abort_button(w, client_data, call_data)
  1087.     Widget w;
  1088.     XtPointer client_data;
  1089.     XtPointer call_data;
  1090. {
  1091.     Widget dialog = (Widget) client_data;
  1092.  
  1093.     Strcpy(getline_input, CANCEL_STR);
  1094.     nh_XtPopdown(XtParent(dialog));
  1095.     exit_x_event = TRUE;
  1096. }
  1097.  
  1098.  
  1099. void
  1100. X11_getlin(question, input)
  1101.     const char *question;
  1102.     char *input;
  1103. {
  1104.     static boolean need_to_init = True;
  1105.  
  1106.     getline_input = input;
  1107.  
  1108.     flush_screen(1);
  1109.     if (need_to_init) {
  1110.     Arg args[1];
  1111.  
  1112.     need_to_init = False;
  1113.  
  1114.     XtSetArg(args[0], XtNallowShellResize, True);
  1115.  
  1116.     getline_popup = XtCreatePopupShell("getline",transientShellWidgetClass,
  1117.                    toplevel, args, ONE);
  1118.     XtOverrideTranslations(getline_popup,
  1119.         XtParseTranslationTable("<Message>WM_PROTOCOLS: getline_delete()"));
  1120.  
  1121.     getline_dialog = CreateDialog(getline_popup, "dialog",
  1122.                     done_button, abort_button);
  1123.  
  1124.     XtRealizeWidget(getline_popup);
  1125.     XSetWMProtocols(XtDisplay(getline_popup), XtWindow(getline_popup),
  1126.             &wm_delete_window, 1);
  1127.     }
  1128.     SetDialogPrompt(getline_dialog, (String)question);    /* set prompt */
  1129.     SetDialogResponse(getline_dialog, "");    /* set default answer */
  1130.     positionpopup(getline_popup, TRUE);        /* center,bottom */
  1131.  
  1132.     nh_XtPopup(getline_popup, (int)XtGrabNone, getline_dialog);
  1133.  
  1134.     /* The callback will enable the event loop exit. */
  1135.     (void) x_event(EXIT_ON_EXIT);
  1136. }
  1137.  
  1138.  
  1139. /* Display file ------------------------------------------------------------ */
  1140. static const char display_translations[] =
  1141.     "#override\n\
  1142.      <Key>q: dismiss_file()\n\
  1143.      <Key>Escape: dismiss_file()\n\
  1144.      <BtnDown>: dismiss_file()";
  1145.  
  1146.  
  1147. /* WM_DELETE_WINDOW callback for file dismissal. */
  1148. /*ARGSUSED*/
  1149. static void
  1150. delete_file(w, event, params, num_params)
  1151.     Widget w;
  1152.     XEvent *event;
  1153.     String *params;
  1154.     Cardinal *num_params;
  1155. {
  1156.     nh_XtPopdown(w);
  1157.     XtDestroyWidget(w);
  1158. }
  1159.  
  1160. /* Callback for file dismissal. */
  1161. /*ARGSUSED*/
  1162. static void
  1163. dismiss_file(w, event, params, num_params)
  1164.     Widget w;
  1165.     XEvent *event;
  1166.     String *params;
  1167.     Cardinal *num_params;
  1168. {
  1169.     Widget popup = XtParent(w);
  1170.     nh_XtPopdown(popup);
  1171.     XtDestroyWidget(popup);
  1172. }
  1173.  
  1174. void
  1175. X11_display_file(str, complain)
  1176.     const char *str;
  1177.     boolean complain;
  1178. {
  1179.     FILE *fp;
  1180.     Arg args[12];
  1181.     Cardinal num_args;
  1182.     Widget popup, dispfile;
  1183.     Position top_margin, bottom_margin, left_margin, right_margin;
  1184.     XFontStruct *fs;
  1185.     int new_width, new_height;
  1186. #define LLEN 128
  1187.     char line[LLEN];
  1188.     int num_lines;
  1189.  
  1190.     /* Use the port-independent file opener to see if the file exists. */
  1191.     fp = fopen_datafile(str, "r");
  1192.  
  1193.     if (!fp) {
  1194.     if(complain) pline("Cannot open %s.  Sorry.", str);
  1195.  
  1196.     return;    /* it doesn't exist, ignore */
  1197.     }
  1198.  
  1199.     /*
  1200.      * Count the number of lines in the file.  If under the max display
  1201.      * size, use that instead.
  1202.      */
  1203.     num_lines = 0;
  1204.     while (fgets(line, LLEN, fp)) {
  1205.     num_lines++;
  1206.     if (num_lines >= DISPLAY_FILE_SIZE) break;
  1207.     }
  1208.  
  1209.     (void) fclose(fp);
  1210.  
  1211.     /* Ignore empty files */
  1212.     if (num_lines == 0) return;
  1213.  
  1214.     num_args = 0;
  1215.     XtSetArg(args[num_args], XtNtitle, str);    num_args++;
  1216.  
  1217.     popup = XtCreatePopupShell("display_file", topLevelShellWidgetClass,
  1218.                            toplevel, args, num_args);
  1219.     XtOverrideTranslations(popup,
  1220.     XtParseTranslationTable("<Message>WM_PROTOCOLS: delete_file()"));
  1221.  
  1222.     num_args = 0;
  1223.     XtSetArg(args[num_args], XtNscrollHorizontal,
  1224.                 XawtextScrollWhenNeeded);    num_args++;
  1225.     XtSetArg(args[num_args], XtNscrollVertical,
  1226.                 XawtextScrollWhenNeeded);    num_args++;
  1227.     XtSetArg(args[num_args], XtNtype, XawAsciiFile);        num_args++;
  1228.     XtSetArg(args[num_args], XtNstring, str);            num_args++;
  1229.     XtSetArg(args[num_args], XtNdisplayCaret, False);        num_args++;
  1230.     XtSetArg(args[num_args], XtNtranslations,
  1231.     XtParseTranslationTable(display_translations));        num_args++;
  1232.  
  1233.     dispfile = XtCreateManagedWidget(
  1234.             "text",            /* name */
  1235.             asciiTextWidgetClass,
  1236.             popup,            /* parent widget */
  1237.             args,            /* set some values */
  1238.             num_args);        /* number of values to set */
  1239.  
  1240.     /* Get font and border information. */
  1241.     num_args = 0;
  1242.     XtSetArg(args[num_args], XtNfont,          &fs);           num_args++;
  1243.     XtSetArg(args[num_args], XtNtopMargin,    &top_margin);    num_args++;
  1244.     XtSetArg(args[num_args], XtNbottomMargin, &bottom_margin); num_args++;
  1245.     XtSetArg(args[num_args], XtNleftMargin,   &left_margin);   num_args++;
  1246.     XtSetArg(args[num_args], XtNrightMargin,  &right_margin);  num_args++;
  1247.     XtGetValues(dispfile, args, num_args);
  1248.  
  1249.     /*
  1250.      * Font height is ascent + descent.
  1251.      *
  1252.      * The data files are currently set up assuming an 80 char wide window
  1253.      * and a fixed width font.  Soo..
  1254.      */
  1255.     new_height = num_lines * (fs->ascent + fs->descent) +
  1256.                         top_margin + bottom_margin;
  1257.     new_width  = 80 * fs->max_bounds.width + left_margin + right_margin;
  1258.  
  1259.     /* Set the new width and height. */
  1260.     num_args = 0;
  1261.     XtSetArg(args[num_args], XtNwidth,  new_width);  num_args++;
  1262.     XtSetArg(args[num_args], XtNheight, new_height); num_args++;
  1263.     XtSetValues(dispfile, args, num_args);
  1264.  
  1265.     nh_XtPopup(popup, (int)XtGrabNone, None);
  1266. }
  1267.  
  1268.  
  1269. /* yn_function ------------------------------------------------------------- */
  1270. /* (not threaded) */
  1271.  
  1272. static const char *yn_quitchars = " \n\r";
  1273. static const char *yn_choices;    /* string of acceptable input */
  1274. static char yn_def;
  1275. static char yn_return;        /* return value */
  1276. static char yn_esc_map;        /* ESC maps to this char. */
  1277. static Widget yn_popup;        /* popup for the yn fuction (created once) */
  1278. static Widget yn_label;        /* label for yn function (created once) */
  1279. static boolean yn_getting_num;    /* TRUE if accepting digits */
  1280. static int yn_ndigits;        /* digit count */
  1281. static long yn_val;        /* accumulated value */
  1282.  
  1283. static const char yn_translations[] =
  1284.     "#override\n\
  1285.      <Key>: yn_key()";
  1286.  
  1287. /*
  1288.  * Convert the given key event into a character.  If the key maps to
  1289.  * more than one character only the first is returned.  If there is
  1290.  * no conversion (i.e. just the CTRL key hit) a NULL is returned.
  1291.  */
  1292. char
  1293. key_event_to_char(key)
  1294.     XKeyEvent *key;
  1295. {
  1296.     char keystring[MAX_KEY_STRING];
  1297.     int nbytes;
  1298.     boolean meta = !!(key->state & Mod1Mask);
  1299.  
  1300.     nbytes = XLookupString(key, keystring, MAX_KEY_STRING,
  1301.                (KeySym *)0, (XComposeStatus *)0);
  1302.  
  1303.     /* Modifier keys return a zero lengh string when pressed. */
  1304.     if (nbytes == 0) return '\0';
  1305.  
  1306.     return (char) (((int) keystring[0]) + (meta ? 0x80 : 0));
  1307. }
  1308.  
  1309. /*
  1310.  * Called when we get a WM_DELETE_WINDOW event on a yn window.
  1311.  */
  1312. /* ARGSUSED */
  1313. static void
  1314. yn_delete(w, event, params, num_params)
  1315.     Widget w;
  1316.     XEvent *event;
  1317.     String *params;
  1318.     Cardinal *num_params;
  1319. {
  1320.     yn_getting_num = FALSE;
  1321.     /* Only use yn_esc_map if we have choices.  Otherwise, return ESC. */
  1322.     yn_return = yn_choices ? yn_esc_map : '\033';
  1323.     exit_x_event = TRUE;    /* exit our event handler */
  1324. }
  1325.  
  1326. /*
  1327.  * Called when we get a key press event on a yn window.
  1328.  */
  1329. /* ARGSUSED */
  1330. static void
  1331. yn_key(w, event, params, num_params)
  1332.     Widget w;
  1333.     XEvent *event;
  1334.     String *params;
  1335.     Cardinal *num_params;
  1336. {
  1337.     char ch;
  1338.  
  1339.     if(appResources.slow && !input_func)
  1340.     map_input(w, event, params, num_params);
  1341.  
  1342.     ch = key_event_to_char((XKeyEvent *) event);
  1343.  
  1344.     if (ch == '\0') {    /* don't accept nul char or modifier event */
  1345.     /* no bell */
  1346.     return;
  1347.     }
  1348.  
  1349.     if (!yn_choices) {            /* accept any input */
  1350.     yn_return = ch;
  1351.     } else {
  1352.     ch = lowc(ch);            /* move to lower case */
  1353.  
  1354.     if (ch == '\033') {
  1355.         yn_getting_num = FALSE;
  1356.         yn_return = yn_esc_map;
  1357.     } else if (index(yn_quitchars, ch)) {
  1358.         yn_return = yn_def;
  1359.     } else if (index(yn_choices, ch)) {
  1360.         if (ch == '#') {
  1361.         if (yn_getting_num) {    /* don't select again */
  1362.             X11_nhbell();
  1363.             return;
  1364.         }
  1365.         yn_getting_num = TRUE;
  1366.         yn_ndigits = 0;
  1367.         yn_val = 0;
  1368.         return;            /* wait for more input */
  1369.         }
  1370.         yn_return = ch;
  1371.         if (ch != 'y') yn_getting_num = FALSE;
  1372.     } else {
  1373.         if (yn_getting_num) {
  1374.         if (digit(ch)) {
  1375.             yn_ndigits++;
  1376.             yn_val = (yn_val * 10) + (long) (ch - '0');
  1377.             return;            /* wait for more input */
  1378.         }
  1379.         if (yn_ndigits && (ch == '\b' || ch == 127/*DEL*/)) {
  1380.             yn_ndigits--;
  1381.             yn_val = yn_val/ 10;
  1382.             return;            /* wait for more input */
  1383.         }
  1384.         }
  1385.         X11_nhbell();        /* no match */
  1386.         return;
  1387.     }
  1388.  
  1389.     if (yn_getting_num) {
  1390.         yn_return = '#';
  1391.         if (yn_val < 0) yn_val = 0;
  1392.         yn_number = yn_val;    /* assign global */
  1393.     }
  1394.     }
  1395.     exit_x_event = TRUE;    /* exit our event handler */
  1396. }
  1397.  
  1398.  
  1399. char
  1400. X11_yn_function(ques, choices, def)
  1401.     const char *ques;
  1402.     const char *choices;
  1403.     char def;
  1404. {
  1405.     static Boolean need_to_init = True;
  1406.     char buf[QBUFSZ];
  1407.     Arg args[4];
  1408.     Cardinal num_args;
  1409.  
  1410.     yn_choices = choices;    /* set up globals for callback to use */
  1411.     yn_def     = def;
  1412.  
  1413.     /*
  1414.      * This is sort of a kludge.  There are quite a few places in the main
  1415.      * nethack code where a pline containing information is followed by a
  1416.      * call to yn_function().  There is no flush of the message window
  1417.      * (it is implicit in the tty window port), so the line never shows
  1418.      * up for us!  Solution: do our own flush.
  1419.      */
  1420.     if (WIN_MESSAGE != WIN_ERR)
  1421.     display_message_window(&window_list[WIN_MESSAGE]);
  1422.  
  1423.     if (choices) {
  1424.     /* ques [choices] (def) */
  1425.     if ((1 + strlen(ques) + 2 + strlen(choices) + 4) >= QBUFSZ)
  1426.         panic("yn_function:  question too long");
  1427.     if (def)
  1428.         Sprintf(buf, "%s [%s] (%c)", ques, choices, def);
  1429.     else
  1430.         Sprintf(buf, "%s [%s] ", ques, choices);
  1431.  
  1432.     /* escape maps to 'q' or 'n' or default, in that order */
  1433.     yn_esc_map = (index(choices, 'q') ? 'q' :
  1434.              (index(choices, 'n') ? 'n' :
  1435.                         def));
  1436.     } else {
  1437.     if ((1 + strlen(ques)) >= QBUFSZ)
  1438.         panic("yn_function:  question too long");
  1439.     Strcpy(buf, ques);
  1440.     }
  1441.  
  1442.     if (!appResources.slow && need_to_init) {
  1443.     need_to_init = False;
  1444.  
  1445.     XtSetArg(args[0], XtNallowShellResize, True);
  1446.     yn_popup = XtCreatePopupShell("query", transientShellWidgetClass,
  1447.                     toplevel, args, ONE);
  1448.     XtOverrideTranslations(yn_popup,
  1449.         XtParseTranslationTable("<Message>WM_PROTOCOLS: yn_delete()"));
  1450.  
  1451.     num_args = 0;
  1452.     XtSetArg(args[num_args], XtNtranslations,
  1453.         XtParseTranslationTable(yn_translations));    num_args++;
  1454.     yn_label = XtCreateManagedWidget("yn_label",
  1455.                 labelWidgetClass,
  1456.                 yn_popup,
  1457.                 args, num_args);
  1458.  
  1459.     XtRealizeWidget(yn_popup);
  1460.     XSetWMProtocols(XtDisplay(yn_popup), XtWindow(yn_popup),
  1461.             &wm_delete_window, 1);
  1462.     }
  1463.  
  1464.     if(appResources.slow)
  1465.     input_func = yn_key;
  1466.  
  1467.     num_args = 0;
  1468.     XtSetArg(args[num_args], XtNlabel, buf);    num_args++;
  1469.     XtSetValues(yn_label, args, num_args);
  1470.  
  1471.     if(!appResources.slow) {
  1472.     /*
  1473.      * Due to some kind of weird bug in the X11R4 and X11R5 shell, we
  1474.      * need to set the label twice to get the size to change.
  1475.      */
  1476.     num_args = 0;
  1477.     XtSetArg(args[num_args], XtNlabel, buf); num_args++;
  1478.     XtSetValues(yn_label, args, num_args);
  1479.  
  1480.     positionpopup(yn_popup, TRUE);
  1481.     nh_XtPopup(yn_popup, (int)XtGrabExclusive, yn_label);
  1482.     }
  1483.  
  1484.     yn_getting_num = FALSE;
  1485.     (void) x_event(EXIT_ON_EXIT);
  1486.  
  1487.     if(appResources.slow) {
  1488.     input_func = 0;
  1489.     num_args = 0;
  1490.     XtSetArg(args[num_args], XtNlabel, " ");    num_args++;
  1491.     XtSetValues(yn_label, args, num_args);
  1492.     } else {
  1493.     nh_XtPopdown(yn_popup);    /* this removes the event grab */
  1494.     }
  1495.  
  1496.     return yn_return;
  1497. }
  1498.  
  1499. /* End global functions ==================================================== */
  1500.  
  1501. /*
  1502.  * Before we wait for input via nhgetch() and nh_poskey(), we need to
  1503.  * do some pre-processing.
  1504.  */
  1505. static int
  1506. input_event(exit_condition)
  1507.     int exit_condition;
  1508. {
  1509.     if (WIN_STATUS != WIN_ERR)    /* hilighting on the fancy status window */
  1510.     check_turn_events();
  1511.     if (WIN_MAP != WIN_ERR)    /* make sure cursor is not clipped */
  1512.     check_cursor_visibility(&window_list[WIN_MAP]);
  1513.     if (WIN_MESSAGE != WIN_ERR)    /* reset pause line */
  1514.     set_last_pause(&window_list[WIN_MESSAGE]);
  1515.  
  1516.     return x_event(exit_condition);
  1517. }
  1518.  
  1519.  
  1520. /*ARGSUSED*/
  1521. void
  1522. msgkey(w, data, event)
  1523.     Widget w;
  1524.     XtPointer data;
  1525.     XEvent *event;
  1526. {
  1527.     Cardinal num = 0;
  1528.     map_input(window_list[WIN_MAP].w, event, (String*) 0, &num);
  1529. }
  1530.  
  1531. /*ARGSUSED*/
  1532. static void
  1533. win_visible(w, data, event, flag)    /* only called for autofocus */
  1534.     Widget w;
  1535.     XtPointer data;    /* client_data not used */
  1536.     XEvent *event;
  1537.     Boolean *flag;    /* continue_to_dispatch flag not used */
  1538. {
  1539.     XVisibilityEvent *vis_event = (XVisibilityEvent *)event;
  1540.  
  1541.     if (vis_event->state != VisibilityFullyObscured) {
  1542.     /* one-time operation; cancel ourself */
  1543.     XtRemoveEventHandler(toplevel, VisibilityChangeMask, False,
  1544.                  win_visible, (XtPointer) 0);
  1545.     /* grab initial input focus */
  1546.     XSetInputFocus(XtDisplay(w), XtWindow(w), RevertToNone, CurrentTime);
  1547.     }
  1548. }
  1549.  
  1550. /*
  1551.  * Set up the playing console.  This has three major parts:  the
  1552.  * message window, the map, and the status window.
  1553.  */
  1554. static void
  1555. init_standard_windows()
  1556. {
  1557.     Widget form, message_viewport, map_viewport, status;
  1558.     Arg args[8];
  1559.     Cardinal num_args;
  1560.     Dimension message_vp_width, map_vp_width, status_width, max_width;
  1561.     int map_vp_hd, status_hd;
  1562.     struct xwindow *wp;
  1563.  
  1564.  
  1565.     num_args = 0;
  1566.     XtSetArg(args[num_args], XtNallowShellResize, True);    num_args++;
  1567.     form = XtCreateManagedWidget("nethack",
  1568.                 formWidgetClass,
  1569.                 toplevel, args, num_args);
  1570.  
  1571.     XtAddEventHandler(form, KeyPressMask, False,
  1572.               (XtEventHandler) msgkey, (XtPointer) 0);
  1573.  
  1574.     if (appResources.autofocus)
  1575.     XtAddEventHandler(toplevel, VisibilityChangeMask, False,
  1576.               win_visible, (XtPointer) 0);
  1577.  
  1578.     /*
  1579.      * Create message window.
  1580.      */
  1581.     WIN_MESSAGE = message_win = find_free_window();
  1582.     wp = &window_list[message_win];
  1583.     wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
  1584.     wp->popup = (Widget) 0;
  1585.     create_message_window(wp, FALSE, form);
  1586.     message_viewport = XtParent(wp->w);
  1587.  
  1588.  
  1589.     /* Tell the form that contains it that resizes are OK. */
  1590.     num_args = 0;
  1591.     XtSetArg(args[num_args], XtNresizable, True);        num_args++;
  1592.     XtSetArg(args[num_args], XtNleft,       XtChainLeft);    num_args++;
  1593.     XtSetArg(args[num_args], XtNtop,       XtChainTop);        num_args++;
  1594.     XtSetValues(message_viewport, args, num_args);
  1595.  
  1596.     if(appResources.slow) {
  1597.     num_args = 0;
  1598.     XtSetArg(args[num_args], XtNtranslations,
  1599.          XtParseTranslationTable(yn_translations)); num_args++;
  1600.     yn_label = XtCreateManagedWidget("yn_label",
  1601.                      labelWidgetClass,
  1602.                      form,
  1603.                      args, num_args);
  1604.     num_args = 0;
  1605.     XtSetArg(args[num_args], XtNfromVert, message_viewport); num_args++;
  1606.     XtSetArg(args[num_args], XtNresizable, True);    num_args++;
  1607.     XtSetArg(args[num_args], XtNlabel, " ");    num_args++;
  1608.     XtSetValues(yn_label, args, num_args);
  1609.     }
  1610.  
  1611.     /*
  1612.      * Create the map window & viewport and chain the viewport beneath the
  1613.      * message_viewport.
  1614.      */
  1615.     map_win = find_free_window();
  1616.     wp = &window_list[map_win];
  1617.     wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
  1618.     wp->popup = (Widget) 0;
  1619.     create_map_window(wp, FALSE, form);
  1620.     map_viewport = XtParent(wp->w);
  1621.  
  1622.     /* Chain beneath message_viewport or yn window. */
  1623.     num_args = 0;
  1624.     if(appResources.slow) {
  1625.     XtSetArg(args[num_args], XtNfromVert, yn_label);    num_args++;
  1626.     } else {
  1627.     XtSetArg(args[num_args], XtNfromVert, message_viewport);num_args++;
  1628.     }
  1629.     XtSetArg(args[num_args], XtNbottom, XtChainBottom);        num_args++;
  1630.     XtSetValues(map_viewport, args, num_args);
  1631.  
  1632.     /* Create the status window, with the form as it's parent. */
  1633.     status_win = find_free_window();
  1634.     wp = &window_list[status_win];
  1635.     wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0;
  1636.     wp->popup = (Widget) 0;
  1637.     create_status_window(wp, FALSE, form);
  1638.     status = wp->w;
  1639.  
  1640.     /*
  1641.      * Chain the status window beneath the viewport.  Mark the left and right
  1642.      * edges so that they stay a fixed distance from the left edge of the
  1643.      * parent, as well as the top and bottom edges so that they stay a fixed
  1644.      * distance from the bottom of the parent.  We do this so that the status
  1645.      * will never expand or contract.
  1646.      */
  1647.     num_args = 0;
  1648.     XtSetArg(args[num_args], XtNfromVert, map_viewport);    num_args++;
  1649.     XtSetArg(args[num_args], XtNleft,      XtChainLeft);        num_args++;
  1650.     XtSetArg(args[num_args], XtNright,      XtChainLeft);        num_args++;
  1651.     XtSetArg(args[num_args], XtNtop,      XtChainBottom);    num_args++;
  1652.     XtSetArg(args[num_args], XtNbottom,      XtChainBottom);    num_args++;
  1653.     XtSetValues(status, args, num_args);
  1654.  
  1655.  
  1656.     /*
  1657.      * Realize the popup so that the status widget knows it's size.
  1658.      *
  1659.      * If we unset MappedWhenManaged then the DECwindow driver doesn't
  1660.      * attach the nethack toplevel to the highest virtual root window.
  1661.      * So don't do it.
  1662.      */
  1663.     /* XtSetMappedWhenManaged(toplevel, False); */
  1664.     XtRealizeWidget(toplevel);
  1665.     wm_delete_window = XInternAtom(XtDisplay(toplevel),
  1666.                    "WM_DELETE_WINDOW", False);
  1667.     XSetWMProtocols(XtDisplay(toplevel), XtWindow(toplevel),
  1668.             &wm_delete_window, 1);
  1669.  
  1670.     /*
  1671.      * Now get the default widths of the windows.
  1672.      */
  1673.     XtSetArg(args[0], XtNwidth, &message_vp_width);
  1674.     XtGetValues(message_viewport, args, ONE);
  1675.     XtSetArg(args[0], XtNwidth, &map_vp_width);
  1676.     XtSetArg(args[1], XtNhorizDistance, &map_vp_hd);
  1677.     XtGetValues(map_viewport, args, TWO);
  1678.     XtSetArg(args[0], XtNwidth, &status_width);
  1679.     XtSetArg(args[1], XtNhorizDistance, &status_hd);
  1680.     XtGetValues(status, args, TWO);
  1681.  
  1682.     /*
  1683.      * Adjust positions and sizes.  The message viewport widens out to the
  1684.      * widest width.  Both the map and status are centered by adjusting
  1685.      * their horizDistance.
  1686.      */
  1687.     if (map_vp_width < status_width || map_vp_width < message_vp_width) {
  1688.     if (status_width > message_vp_width) {
  1689.         XtSetArg(args[0], XtNwidth, status_width);
  1690.         XtSetValues(message_viewport, args, ONE);
  1691.         max_width = status_width;
  1692.     } else {
  1693. /***** The status display looks better when left justified.
  1694.         XtSetArg(args[0], XtNhorizDistance,
  1695.                 status_hd+((message_vp_width-status_width)/2));
  1696.         XtSetValues(status, args, ONE);
  1697. *****/
  1698.         max_width = message_vp_width;
  1699.     }
  1700.     XtSetArg(args[0], XtNhorizDistance, map_vp_hd+((int)(max_width-map_vp_width)/2));
  1701.     XtSetValues(map_viewport, args, ONE);
  1702.  
  1703.     } else {    /* map is widest */
  1704.     XtSetArg(args[0], XtNwidth, map_vp_width);
  1705.     XtSetValues(message_viewport, args, ONE);
  1706.  
  1707. /***** The status display looks better when left justified.
  1708.     XtSetArg(args[0], XtNhorizDistance,
  1709.                 status_hd+((map_vp_width-status_width)/2));
  1710.  
  1711.     XtSetValues(status, args, ONE);
  1712. *****/
  1713.     }
  1714.     /*
  1715.      * Clear all data values on the fancy status widget so that the values
  1716.      * used for spacing don't appear.  This needs to be called some time
  1717.      * after the fancy status widget is realized (above, with the game popup),
  1718.      * but before it is popped up.
  1719.      */
  1720.     null_out_status();
  1721.     /*
  1722.      * Set the map size to its standard size.  As with the message window
  1723.      * above, the map window needs to be set to its constrained size until
  1724.      * its parent (the viewport widget) was realized.
  1725.      *
  1726.      * Move the message window's slider to the bottom.
  1727.      */
  1728.     set_map_size(&window_list[map_win], COLNO, ROWNO);
  1729.     set_message_slider(&window_list[message_win]);
  1730.  
  1731.     /* attempt to catch fatal X11 errors before the program quits */
  1732.     (void) XtAppSetErrorHandler(app_context, (XtErrorHandler) hangup);
  1733.  
  1734.     /* We can now print to the message window. */
  1735.     flags.window_inited = 1;
  1736. }
  1737.  
  1738.  
  1739. void
  1740. nh_XtPopup(w, g, childwid)
  1741.     Widget w;        /* widget */
  1742.     int    g;        /* type of grab */
  1743.     Widget childwid;    /* child to recieve focus (can be None) */
  1744. {
  1745.     XtPopup(w, (XtGrabKind)g);
  1746.     XSetWMProtocols(XtDisplay(w), XtWindow(w), &wm_delete_window, 1);
  1747.     if (appResources.autofocus) XtSetKeyboardFocus(toplevel, childwid);
  1748. }
  1749.  
  1750. void
  1751. nh_XtPopdown(w)
  1752.     Widget w;
  1753. {
  1754.     XtPopdown(w);
  1755.     if (appResources.autofocus) XtSetKeyboardFocus(toplevel, None);
  1756. }
  1757.  
  1758. void
  1759. win_X11_init()
  1760. {
  1761. #ifdef OPENWINBUG
  1762.     /* With the OpenWindows 3.0 libraries and the SunOS 4.1.2 ld, these
  1763.      * two routines will not be found when linking.  An apparently correct
  1764.      * executable is produced, along with nasty messages and a failure code
  1765.      * returned to make.  The routines are in the static libXmu.a and
  1766.      * libXmu.sa.4.0, but not in libXmu.so.4.0.  Rather than fiddle with
  1767.      * static linking, we do this.
  1768.      */
  1769.     if (rn2(2) > 2) {
  1770.     /* i.e., FALSE that an optimizer probably can't find */
  1771.     get_wmShellWidgetClass();
  1772.     get_applicationShellWidgetClass();
  1773.     }
  1774. #endif
  1775.     return;
  1776. }
  1777.  
  1778. /*winX.c*/
  1779.